package com.skyline.base.controller;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import net.imagine.core.ResultState;
import net.imagine.core.engine.ImageEngine;
import net.imagine.provider.skyline.ImageResizeResult;
import net.imagine.provider.skyline.LocalImageResizeTask;
import net.imagine.provider.skyline.MultipartImageResizeTask;
import net.imagine.provider.skyline.SkylineImageResizeTask;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang.time.DateUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.RedirectView;

import com.skyline.base.exception.NoOperatePermissionException;
import com.skyline.base.exception.NoResourceException;
import com.skyline.base.exception.NoVisitPermissionException;
import com.skyline.base.exception.NotLoginException;
import com.skyline.base.exception.OperateFailedException;
import com.skyline.base.model.Footprint;
import com.skyline.base.model.Resource;
import com.skyline.base.service.FootprintService;
import com.skyline.base.type.ResourceType;
import com.skyline.common.exception.BusinessException;
import com.skyline.common.util.AuthorityUtil;
import com.skyline.common.util.Constant;
import com.skyline.common.util.WebHelper;
import com.skyline.common.validation.Errors;
import com.skyline.common.validation.ValidationUtils;
import com.skyline.user.model.User;
import com.skyline.wo.model.Album;
import com.skyline.wo.model.Photo;
import com.skyline.wo.service.AlbumService;

/**
 * Controller的基础类，所有Controller都需要继承自此类
 * 
 * @author wuqh
 * 
 */
public class BaseController {
	private static final Log LOGGER = LogFactory.getLog(BaseController.class);
	protected static final String URL_SUFFIX = ".html";

	@Autowired
	protected FootprintService footprintService;
	@Autowired
	protected AlbumService albumService;
	@Autowired
	protected ImageEngine imagine;
	protected @Value("${imagine.localStorePath}")
	String localStorePath;
	protected @Value("${imagine.baseSize1},${imagine.baseSize2}")
	int[] baseSizes;
	protected @Value("${album.uploadphoto.filesize}")
	long maxSizePerFile;

	protected String buildRecirectPath(String baseUrl) {
		HttpServletRequest request = WebHelper.initRequest(null);
		return request.getContextPath() + baseUrl + URL_SUFFIX;
	}

	/**
	 * 处理业务类异常
	 * 
	 * @param request
	 * @param ex
	 * @return
	 */
	@ExceptionHandler(BusinessException.class)
	protected ModelAndView handlerBusinsessException(HttpServletRequest request, BusinessException ex) {
		User user = WebHelper.getSessionAttribute(request, Constant.SESSION_USER);
		StringBuffer requestUrl = request.getRequestURL();
		String queryString = request.getQueryString();
		requestUrl.append("?").append(queryString);
		String logMessage = exceptionLog(request, user, requestUrl);

		ModelAndView view = new ModelAndView("errMsg");

		if (ex instanceof NoVisitPermissionException) {
			LOGGER.warn(logMessage + "业务异常[没有访问权限]，异常原因：" + ex.getMessage());
			view.addObject("reasone", "你没有权限查看该内容");
		} else if (ex instanceof NoResourceException) {
			LOGGER.warn("业务异常[访问的资源不存在]，异常原因：" + ex.getMessage());
			view.addObject("reasone", "内容过期、内容不存在");
		} else if (ex instanceof NotLoginException) {
			LOGGER.warn("业务异常[没有登录]");
			String requestFrom = null;
			try {
				requestFrom = URLEncoder.encode(requestUrl.toString(), "UTF-8");
			} catch (UnsupportedEncodingException e) {
				LOGGER.debug("系统不支持UTF-8编码", e);
			}
			String url = buildRecirectPath("/login");
			url = url + "?" + Constant.REQUEST_FROM + "=" + requestFrom;
			return new ModelAndView(new RedirectView(url));
		} else if (ex instanceof NoOperatePermissionException) {
			// 如果没有操作权限而且没有登录，则需要重新登录，而且登录后直接进入首页
			// 对于删除类操作即使没有登录，重新登录后也不能继续做删除操作
			LOGGER.warn("业务异常[没有操作权限]，异常原因：" + ex.getMessage());
			if (user == null) {
				return new ModelAndView(new RedirectView(buildRecirectPath("/login")));
			}
			view.addObject("reasone", "你没有权限操作该内容");
		} else if (ex instanceof OperateFailedException) {
			LOGGER.warn("业务异常[操作失败]，异常原因" + ex.getMessage());
			view.addObject("reasone", "由于网络等原因导致的操作失败");
		} else {
			LOGGER.warn("业务异常[一般业务异常]，异常原因：", ex);
			view.addObject("reasone", "内容过期、内容不存在，你没有权限查看该内容");
		}

		return view;
	}

	/**
	 * 处理其他未知异常
	 * 
	 * @param request
	 * @param e
	 * @return
	 */
	@ExceptionHandler(Exception.class)
	protected ModelAndView handlerException(HttpServletRequest request, Exception e) {
		User user = WebHelper.getSessionAttribute(request, Constant.SESSION_USER);
		StringBuffer requestUrl = request.getRequestURL();
		String logMessage = exceptionLog(request, user, requestUrl);

		if (e instanceof BindException) {
			LOGGER.error(logMessage + "Spring绑定参数错误，可能地址中的参数不合法：", e);
		} else {
			LOGGER.error(logMessage + "异常原因：", e);
		}
		return new ModelAndView("error");
	}

	private String exceptionLog(HttpServletRequest request, User user, StringBuffer requestUrl) {
		if (user == null) {
			return "IP为[" + WebHelper.getClientIp(request) + "]的用户访问地址[" + requestUrl.toString() + "]时发生异常。";
		} else {
			return "ID为[" + user.getId() + "]的用户访问地址[" + requestUrl.toString() + "]时发生异常。";
		}
	}

	/**
	 * 表单校验方法 表单校验必须调用此方法
	 * 
	 * @param formName
	 *            validation-xxx.xml中配置的form的name
	 * @param bean
	 *            需要验证的Bean或者Map或者String
	 * @return 如果不空表示校验不通过，在调用后必须调用
	 *         {@link BaseController#processValidationErrors(String, String, ModelAndView)}
	 * 
	 * */
	protected <T> String validateForm(String formName, T bean) {
		Errors errors = ValidationUtils.validateForm(formName, bean);
		String[] errorMsgs = ValidationUtils.getAllErrorMessages(errors);
		if (errorMsgs.length < 1) {
			return null;
		} else {
			return errorMsgs[0];
		}
	}

	/**
	 * 表单验证失败的处理
	 * 
	 * @param msgAttr
	 *            错误信息设置到returnMav的model中
	 * @param errMsg
	 *            错误信息
	 * @param 错误需要返回的ModelAndView
	 * @return 返回的设置后的returnMav
	 * */
	protected ModelAndView processValidationErrors(String msgAttr, String errMsg, ModelAndView returnView) {
		if (errMsg != null) {
			returnView.addObject(msgAttr, errMsg);
		}
		return returnView;
	}

	/**
	 * 检测用户是否登录，如果登录返回此用户，否则抛出未登录异常
	 * 
	 * @return
	 */
	protected User checkAndReturnLoginedUser() {
		User user = WebHelper.getSessionAttribute(null, Constant.SESSION_USER);
		if (user == null) {
			throw new NotLoginException();
		} else {
			return user;
		}
	}

	/**
	 * 检查用户所属权限是否能访问资源,如果有返回权限等级，如果权限不够抛出无权限异常，如果资源不存在抛出资源不存在异常
	 * 
	 * @param authority
	 * @param resource
	 * @return
	 */
	protected int checkAndReturnAuthority(Resource resource) {
		if (resource != null) {
			int authority = AuthorityUtil.getAuthority(null, resource.getOwnerId());
			int resourceAuth = resource.getAuthority();
			if (authority < resourceAuth) {
				throw new NoVisitPermissionException("用户没有访问ID为:" + resource.getId() + "资源["
						+ resource.getClass().getName() + "]的权限");
			} else {
				return authority;
			}
		} else {
			throw new NoResourceException("你访问资源不存在");
		}
	}

	protected Map<String, Object> toJsonMap(boolean success, String errMsg, boolean logined) {
		Map<String, Object> result = new HashMap<String, Object>();
		result.put("success", Boolean.valueOf(success));
		result.put("errmsg", errMsg);
		result.put("logined", Boolean.valueOf(logined));
		return result;
	}

	protected long getViewerId() {
		User user = WebHelper.getSessionAttribute(null, Constant.SESSION_USER);
		if (user == null) {
			return 0L;
		} else {
			return user.getId();
		}
	}

	protected boolean checkVisitedAndLogFootprint(long resourceId, ResourceType type) {
		User user = WebHelper.getSessionAttribute(null, Constant.SESSION_USER);
		long viewerId = 0L;
		if (user == null) {//未登录用户始终已访问，不增加浏览量
			return true;
		} else {
			viewerId = user.getId();
		}
		Footprint footprint = footprintService.getUserFootprintOfResource(viewerId, resourceId, type);
		if(footprint == null) {
			try {
				footprintService.createFootprint(user, resourceId, type);
			} catch (Exception e) {
				LOGGER.info("无法创建脚印", e);
			}
			return false;
		}
		Date lastVisitDate = footprint.getLastVisitTime();
		Date today = new Date();
		if(!DateUtils.isSameDay(lastVisitDate, today)) {//如果不是同一天需要更新脚印
			try {
				footprintService.updateFootprint(user, resourceId, type);
			} catch (Exception e) {
				LOGGER.info("无法更新脚印", e);
			}
		}
		return true;
	}

	protected List<ImageResizeResult> processResizeResult(List<ImageResizeResult> results, StringBuilder message) {
		List<ImageResizeResult> filesInfo = new ArrayList<ImageResizeResult>(results.size());
		for (ImageResizeResult result : results) {
			ResultState state = result.getResultState();
			switch (state) {
			case SUCCESS:
				filesInfo.add(result);
				break;
			case NOT_IMAGE:
				message.append("上传图片" + result.getOriginalFilename() + "失败，失败原因：图片格式不支持。<br/>");
				break;
			default:
				message.append("上传图片" + result.getOriginalFilename() + "失败。<br/>");
				break;
			}
		}
		return filesInfo;
	}

	@SuppressWarnings("rawtypes")
	protected List<SkylineImageResizeTask> prepareResizeTask(List files, long userId, long albumId,
			StringBuilder message) {
		if (CollectionUtils.isEmpty(files)) {
			return null;
		}
		boolean isLocal = !(files.get(0) instanceof MultipartFile);
		List<SkylineImageResizeTask> tasks = new ArrayList<SkylineImageResizeTask>(files.size());
		for (Object file : files) {
			long fileSize = 0L;
			String filename = null;
			SkylineImageResizeTask task;

			if (isLocal) {
				File localFile = (File) file;
				fileSize = localFile.length();
				filename = FilenameUtils.getName(localFile.getAbsolutePath());
			} else {
				MultipartFile multipartFile = (MultipartFile) file;
				fileSize = multipartFile.getSize();
				filename = multipartFile.getOriginalFilename();
			}

			if (fileSize == 0) {
				continue;
			}
			if (fileSize > maxSizePerFile) {
				message.append("上传图片" + filename + "失败，失败原因：图片太大。\n");
				continue;
			}

			if (isLocal) {
				File localFile = (File) file;
				task = new LocalImageResizeTask(localFile.getAbsolutePath(), baseSizes);
			} else {
				MultipartFile multipartFile = (MultipartFile) file;
				task = new MultipartImageResizeTask(multipartFile, baseSizes, null, false);
			}

			task.setUserId(userId);
			task.setAlbumId(albumId);

			tasks.add(task);
		}

		return tasks;
	}

	@SuppressWarnings("unchecked")
	protected Map<String, Object> batchUploadOk(Long albumId, String files, User userLiked) {
		Long userId = userLiked.getId();
		String[] filesPath = files.split("\\|");
		List<File> localFiles = new ArrayList<File>(filesPath.length);
		for (String filePath : filesPath) {
			if (!StringUtils.hasLength(filePath)) {
				continue;
			}
			File localFile = new File(filePath);
			localFiles.add(localFile);
		}

		if (localFiles.isEmpty()) {
			return toJsonMap(false, "没有找到需要压缩的文件！", true);
		}

		StringBuilder message = new StringBuilder();
		List<SkylineImageResizeTask> tasks = prepareResizeTask(localFiles, userId, albumId, message);
		if (CollectionUtils.isEmpty(tasks)) {
			return toJsonMap(false, "没有找到需要压缩的文件！", true);
		}

		List<ImageResizeResult> results = imagine.processImage(localStorePath, tasks);
		List<ImageResizeResult> filesInfo = processResizeResult(results, message);

		Album album = albumService.getAlbumForChange(albumId, userId);

		List<Photo> photos = null;
		if (!CollectionUtils.isEmpty(filesInfo)) {
			photos = albumService.createPhotos(userLiked, album, filesInfo);
		}

		Map<Long, List<Photo>> filesMap = (Map<Long, List<Photo>>) WebHelper.getSessionAttribute(null,
				Constant.SESSION_UPLOAD_OK_FILES);
		if (filesMap == null) {
			filesMap = new HashMap<Long, List<Photo>>();
		}
		filesMap.put(album.getId(), photos);
		WebHelper.setSessionAttribute(null, Constant.SESSION_UPLOAD_OK_FILES, filesMap);

		if (StringUtils.hasLength(message)) {// 存在错误
			return toJsonMap(false, message.toString(), true);
		} else {
			return toJsonMap(true, "压缩图片成功！", true);
		}
	}

	@SuppressWarnings("unchecked")
	protected ModelAndView normalUpload(Long albumId, User userLiked, MultipartHttpServletRequest request,
			String inputView, String okViewPath) {
		long userId = userLiked.getId();
		Album album = albumService.getAlbumForChange(albumId, userId);
		List<MultipartFile> files = request.getFiles("file");
		StringBuilder message = new StringBuilder();
		List<SkylineImageResizeTask> tasks = prepareResizeTask(files, userId, albumId, message);
		if (CollectionUtils.isEmpty(tasks)) {
			ModelAndView mav = new ModelAndView(inputView);
			mav.addObject("message", "没有找到您想传的图片");
			mav.addObject("album", album);
			return mav;
		}

		List<ImageResizeResult> results = imagine.processImage(localStorePath, tasks);
		List<ImageResizeResult> filesInfo = processResizeResult(results, message);

		List<Photo> photos = null;
		if (!CollectionUtils.isEmpty(filesInfo)) {
			photos = albumService.createPhotos(userLiked, album, filesInfo);
		}

		ModelAndView view = new ModelAndView(okViewPath);
		view.addObject("album", album);
		view.addObject("message", message);
		Map<Long, List<Photo>> filesMap = (Map<Long, List<Photo>>) WebHelper.getSessionAttribute(request,
				Constant.SESSION_UPLOAD_OK_FILES);
		if (filesMap == null) {
			filesMap = new HashMap<Long, List<Photo>>();
		}
		filesMap.put(album.getId(), photos);
		WebHelper.setSessionAttribute(request, Constant.SESSION_UPLOAD_OK_FILES, filesMap);

		return view;
	}
}
